/**
 * 浮点数精度处理工具函数
 * 解决JavaScript浮点数运算精度问题
 */

/**
 * 获取数字的小数位数
 * @param num 数字
 * @returns 小数位数
 */
function getDecimalPlaces(num: number): number {
  const str = num.toString();
  if (str.indexOf(".") !== -1) {
    return str.split(".")[1].length;
  }
  return 0;
}

/**
 * 将数字转换为整数，用于精确计算
 * @param num 数字
 * @returns 转换后的整数和放大倍数
 */
function toInteger(num: number): { value: number; factor: number } {
  const decimalPlaces = getDecimalPlaces(num);
  const factor = Math.pow(10, decimalPlaces);
  return {
    value: Math.round(num * factor),
    factor: factor,
  };
}

/**
 * 精确加法
 * @param a 加数
 * @param b 被加数
 * @param precision 保留小数位数，默认2位
 * @returns 精确的加法结果
 */
export function add(
  a: number | string,
  b: number | string,
  precision: number = 2
): number {
  const numA = Number(a);
  const numB = Number(b);

  if (isNaN(numA) || isNaN(numB)) {
    throw new Error("参数必须是有效的数字");
  }

  const maxDecimalPlaces = Math.max(
    getDecimalPlaces(numA),
    getDecimalPlaces(numB)
  );
  const factor = Math.pow(10, maxDecimalPlaces);

  const result =
    (Math.round(numA * factor) + Math.round(numB * factor)) / factor;
  return Number(result.toFixed(precision));
}

/**
 * 精确减法
 * @param a 被减数
 * @param b 减数
 * @param precision 保留小数位数，默认2位
 * @returns 精确的减法结果
 */
export function subtract(
  a: number | string,
  b: number | string,
  precision: number = 2
): number {
  const numA = Number(a);
  const numB = Number(b);

  if (isNaN(numA) || isNaN(numB)) {
    throw new Error("参数必须是有效的数字");
  }

  const maxDecimalPlaces = Math.max(
    getDecimalPlaces(numA),
    getDecimalPlaces(numB)
  );
  const factor = Math.pow(10, maxDecimalPlaces);

  const result =
    (Math.round(numA * factor) - Math.round(numB * factor)) / factor;
  return Number(result.toFixed(precision));
}

/**
 * 精确乘法
 * @param a 乘数
 * @param b 被乘数
 * @param precision 保留小数位数，默认2位
 * @returns 精确的乘法结果
 */
export function multiply(
  a: number | string,
  b: number | string,
  precision: number = 2
): number {
  const numA = Number(a);
  const numB = Number(b);

  if (isNaN(numA) || isNaN(numB)) {
    throw new Error("参数必须是有效的数字");
  }

  const intA = toInteger(numA);
  const intB = toInteger(numB);

  const result = (intA.value * intB.value) / (intA.factor * intB.factor);
  return Number(result.toFixed(precision));
}

/**
 * 精确除法
 * @param a 被除数
 * @param b 除数
 * @param precision 保留小数位数，默认2位
 * @returns 精确的除法结果
 */
export function divide(
  a: number | string,
  b: number | string,
  precision: number = 2
): number {
  const numA = Number(a);
  const numB = Number(b);

  if (isNaN(numA) || isNaN(numB)) {
    throw new Error("参数必须是有效的数字");
  }

  if (numB === 0) {
    throw new Error("除数不能为零");
  }

  const intA = toInteger(numA);
  const intB = toInteger(numB);

  const result = (intA.value / intB.value) * (intB.factor / intA.factor);
  return Number(result.toFixed(precision));
}

/**
 * 格式化金额显示
 * @param amount 金额
 * @param precision 小数位数，默认2位
 * @param showCurrency 是否显示货币符号，默认false
 * @returns 格式化后的金额字符串
 */
export function formatAmount(
  amount: number | string,
  precision: number = 2,
  showCurrency: boolean = false
): string {
  const num = Number(amount);

  if (isNaN(num)) {
    return showCurrency ? "¥0.00" : "0.00";
  }

  const formatted = num.toFixed(precision);
  return showCurrency ? `¥${formatted}` : formatted;
}

/**
 * 计算百分比
 * @param part 部分值
 * @param total 总值
 * @param precision 保留小数位数，默认2位
 * @returns 百分比结果
 */
export function percentage(
  part: number | string,
  total: number | string,
  precision: number = 2
): number {
  const numPart = Number(part);
  const numTotal = Number(total);

  if (isNaN(numPart) || isNaN(numTotal) || numTotal === 0) {
    return 0;
  }

  return multiply(divide(numPart, numTotal, precision + 2), 100, precision);
}

/**
 * 安全的数字转换
 * @param value 要转换的值
 * @param defaultValue 默认值，默认为0
 * @returns 转换后的数字
 */
export function safeNumber(value: any, defaultValue: number = 0): number {
  const num = Number(value);
  return isNaN(num) ? defaultValue : num;
}

/**
 * 计算商品总价（数量 × 单价）
 * @param quantity 数量
 * @param price 单价
 * @param precision 保留小数位数，默认2位
 * @returns 总价
 */
export function calculateTotalPrice(
  quantity: number | string,
  price: number | string,
  precision: number = 2
): number {
  return multiply(quantity, price, precision);
}

/**
 * 计算订单总金额
 * @param items 订单项数组，每项包含quantity和price
 * @param precision 保留小数位数，默认2位
 * @returns 订单总金额
 */
export function calculateOrderTotal(
  items: Array<{ quantity: number | string; price: number | string }>,
  precision: number = 2
): number {
  return items.reduce((total, item) => {
    const itemTotal = multiply(item.quantity, item.price, precision);
    return add(total, itemTotal, precision);
  }, 0);
}

/**
 * 比较两个数字是否相等（考虑精度问题）
 * @param a 数字a
 * @param b 数字b
 * @param precision 精度，默认2位小数
 * @returns 是否相等
 */
export function isEqual(
  a: number | string,
  b: number | string,
  precision: number = 2
): boolean {
  const numA = Number(a);
  const numB = Number(b);

  if (isNaN(numA) || isNaN(numB)) {
    return false;
  }

  const factor = Math.pow(10, precision);
  return Math.round(numA * factor) === Math.round(numB * factor);
}

/**
 * 验证是否为有效的金额
 * @param value 要验证的值
 * @param allowZero 是否允许为0，默认true
 * @returns 验证结果
 */
export function isValidAmount(value: any, allowZero: boolean = true): boolean {
  const num = Number(value);

  if (isNaN(num)) {
    return false;
  }

  if (!allowZero && num === 0) {
    return false;
  }

  return num >= 0;
}

/**
 * 将数字限制在指定范围内
 * @param value 数字
 * @param min 最小值
 * @param max 最大值
 * @returns 限制后的数字
 */
export function clamp(
  value: number | string,
  min: number,
  max: number
): number {
  const num = Number(value);

  if (isNaN(num)) {
    return min;
  }

  return Math.max(min, Math.min(max, num));
}

// 默认导出所有函数
export default {
  add,
  subtract,
  multiply,
  divide,
  formatAmount,
  percentage,
  safeNumber,
  calculateTotalPrice,
  calculateOrderTotal,
  isEqual,
  isValidAmount,
  clamp,
};
